//
//  Router.swift
//  Route
//
//  Created by 蒋晨成 on 2021/12/15.
//

import Base
import LogMan
import Standard
import UIKit
import URLRoute

public enum Router {}

public extension Router {
    typealias RoutableFactory = (_ values: [String: Any]) -> ViewController?
    typealias ActableFactory = (_ values: [String: Any]) -> Bool
}

extension Router: TypeName {}

private extension Router {
    static let navigator = URLRouter()
    @DelayInit
    static var scheme: String
    static let schemePattern = "\(scheme)://"
    static let logger = Loggers[typeName]
}

// MARK: - Register

public extension Router {
    static func register(scheme: String) {
        self.scheme = scheme
    }

    static func register(moduleName: String, description: String, factory: @escaping RoutableFactory) {
        let urlPattern = schemePattern + moduleName
        navigator.register(urlPattern) { _, values, _ in
            factory(values)
        }
        logger.debug("registered : \(urlPattern) --- description : \(description)")
    }

    static func register(actorName: String, description: String, action: @escaping ActableFactory) {
        let urlPattern = schemePattern + actorName
        navigator.handle(urlPattern) { _, values, _ in
            action(values)
        }
        logger.debug("handled : \(urlPattern) --- description : \(description)")
    }
}

// MARK: - Open

public extension Router {
    static func open(pattern: String) {
        guard let url = URL(string: schemePattern + pattern) else { return }
        navigator.open(url)
    }

    static func open(url: URL) {
        switch url.scheme {
        case .none: push(pattern: url.path)
        case .some(let scheme):
            switch scheme == self.scheme {
            case true:
                logger.info("open url : \(url)")
                navigator.open(url)
            case false:
                logger.info("can not open url : \(url)")
                logger.notice("try to open :\(url) by Application")
                UIApplication.shared.open(url)
            }
        }
    }

    static func applicationOpen(_ url: URL, options: [UIApplication.OpenExternalURLOptionsKey: Any] = [:], completionHandler: ((Bool) -> Void)? = nil) {
        UIApplication.shared.open(url, options: options, completionHandler: completionHandler)
    }
}

// MARK: - Route

public extension Router {
    static func push(pattern: String) {
        let url = schemePattern + pattern
        push(url: url)
    }

    static func push(url: String) {
        guard let viewController = viewController(of: url) else {
            return logger.error("unable push viewController for url : <\(url)>")
        }
        navigator.push(viewController, from: nil, animated: type(of: viewController).pushAnimated)
        logger.debug("push to \(viewController)")
    }

    static func push(viewController: Routable) {
        navigator.push(viewController, from: nil, animated: type(of: viewController).pushAnimated)
    }
}

public extension Router {
    static func present(pattern: String, from presentation: UIViewController? = nil, completion: (() -> Void)? = nil) {
        let url = schemePattern + pattern
        present(url: url, from: presentation, completion: completion)
    }

    static func present(url: String, from presentation: UIViewController? = nil, completion: (() -> Void)? = nil) {
        guard let viewController = viewController(of: url) else {
            return logger.error("unable present viewController for url : <\(url)>")
        }
        navigator.present(viewController, wrap: nil, from: presentation, animated: type(of: viewController).presentAnimated, completion: completion)
        logger.debug("present to \(viewController)")
    }
}

public extension Router {
    static func addChild(pattern: String, to parentViewController: UIViewController? = nil) {
        let url = schemePattern + pattern
        addChild(url: url, to: parentViewController)
    }

    static func addChild(url: String, to parentViewController: UIViewController? = nil) {
        guard let childController = viewController(of: url) else {
            return logger.error("unable addChild viewController for url : <\(url)>")
        }
        guard let topViewController = parentViewController ?? UIViewController.topMost else {
            return logger.error("unable get topViewController")
        }

        logger.debug("addChild \(childController)")
        topViewController.addChild(childController)
        topViewController.view.addSubview(childController.view)
        childController.didMove(toParent: topViewController)
    }
}

public extension Router {
    static func viewController(of url: String) -> (ViewController & Routable)? {
        guard let viewController = navigator.viewController(for: url) as? ViewController & Routable else {
            return nil
        }
        return viewController
    }
}
